              package com.java.diagnostics.visualizer.parser.tgc;
              
              import com.java.diagnostics.visualizer.data.DataLevel;
              import com.java.diagnostics.visualizer.data.SourceData;
              import com.java.diagnostics.visualizer.data.TupleData;
              import com.java.diagnostics.visualizer.data.TupleDataBuilder;
              import com.java.diagnostics.visualizer.data.axes.AxisPair;
              import com.java.diagnostics.visualizer.data.axes.XAxis;
              import com.java.diagnostics.visualizer.data.axes.XDataAxis;
              import com.java.diagnostics.visualizer.data.axes.YDataAxis;
              import com.java.diagnostics.visualizer.exceptions.GCAndMemoryVisualizerException;
              import com.java.diagnostics.visualizer.factory.DataFactory;
              import com.java.diagnostics.visualizer.factory.SourceDataFactory;
              import com.java.diagnostics.visualizer.gc.defaultextensions.util.Messages;
              import com.java.diagnostics.visualizer.impl.factory.LogFactory;
              import com.java.diagnostics.visualizer.metadata.TupleMetaData;
              import com.java.diagnostics.visualizer.metadata.TupleMetaData.TableType;
              import com.java.diagnostics.visualizer.parser.vgc.constants.VGCAxes;
              import com.java.diagnostics.visualizer.parsers.Parser;
              import com.java.diagnostics.visualizer.properties.OutputProperties;
              import com.java.diagnostics.visualizer.sources.Source;
              import java.io.BufferedReader;
              import java.io.IOException;
              import java.util.HashMap;
              import java.util.Iterator;
              import java.util.Map;
              import java.util.Set;
              import java.util.logging.Level;
              import java.util.logging.Logger;
              import java.util.regex.Matcher;
              import java.util.regex.Pattern;
              
              public class TGCParserImpl
                implements Parser
              {
                private static final String FINAL_CARDS_CLEANED_TOTAL = "TGCParserImpl.final.cards.cleaned.total";
                private static final String FINAL_CARDS_CLEANED_PHASE_2 = "TGCParserImpl.final.cards.cleaned.phase2";
                private static final String FINAL_CARDS_CLEANED_PHASE_1 = "TGCParserImpl.final.cards.cleaned.phase1";
                private static final String CONCURRENT_CARDS_CLEANED_TOTAL = "TGCParserImpl.concurrent.cards.cleaned.total";
                private static final String CONCURRENT_CARDS_CLEANED_PHASE_2 = "TGCParserImpl.concurrent.cards.cleaned.phase2";
                private static final String CONCURRENT_CARDS_CLEANED_PHASE_1 = "TGCParserImpl.concurrent.cards.cleaned.phase1";
                private static final String TLH_ALLOCATIONS = "TGCParserImpl.tlh.allocations";
                private static final String ALLOCATED_TLH_DISCARD = "TGCParserImpl.allocated.tlh.discard";
                private static final String ALLOCATED_TLH_SIZE = "TGCParserImpl.allocated.tlh.size";
                private static final String NON_TLH_ALLOCATIONS = "TGCParserImpl.non.tlh.allocations";
                private static final String NON_TLH_ALLOCATION_SIZE = "TGCParserImpl.non.tlh.size";
                private static final String NON_TLH_ALLOCATION_DISCARD = "TGCParserImpl.non.tlh.discard";
                private static final String NON_TLH_ALLOCATION_SEARCH = "TGCParserImpl.non.tlh.search";
                private static final String TOTAL_ALLOCATIONS = "TGCParserImpl.total.allocations";
                private static final String[] ALLOCATIONS_PAYING_TAX = { 
                  "TGCParserImpl.allocations.0.tax", "TGCParserImpl.allocations.0.tax", 
                  "TGCParserImpl.allocations.25.tax", "TGCParserImpl.allocations.50.tax", 
                  "TGCParserImpl.allocations.75.tax", "TGCParserImpl.allocations.100.tax" };
                private static final int UNSET = -1;
                private static final String TGC_PARSER_DOESN_T_KNOW_HOW_TO_PARSE = "TGC parser doesn't know how to parse ";
                private final String className = getClass().getName();
                private static final String EMPTY_STRING = "";
                private static final String TLH_ALLOCATION_ATTRIBUTES = "tlh allocation";
                private static final String CARD_CLEANING_FOR_LINE_START = "Card cleaning for";
                private static final String CONCURRENT_MARK_ANALYSIS_LINE_START = "Concurrent mark analysis";
                private static final String ALLOC_LINE_START = "<Alloc";
                private static final Pattern ALLOC_REGEX = Pattern.compile("<Alloc TLH: count ([0-9][0-9]*), size ([0-9][0-9]*),.*discard ([0-9][0-9]*) *>");
              
                private static final Pattern NON_TLH_ALLOC_REGEX = Pattern.compile(".*non-TLH: count ([0-9][0-9]*), search ([0-9][0-9]*), size ([0-9][0-9]*), discard ([0-9][0-9]*) *>");
              
                private static final Pattern CONCURRENT_ANALYSIS_REGEX = Pattern.compile("Concurrent mark analysis: Total Allocations: ([0-9][0-9]*) Tax Paid 0%: ([0-9][0-9]*) 25%: ([0-9][0-9]*) 50%: ([0-9][0-9]*)  *75%: ([0-9][0-9]*) 100%\\+: ([0-9][0-9]*)");
              
                private static final Pattern CARD_CLEANING_REGEX = Pattern.compile("Card cleaning for GC\\(([0-9][0-9]*)\\)");
              
                private static final Pattern CARD_CLEANING_REGEX_3 = Pattern.compile("  concurrent cards cleaned: Phase1= \"([0-9][0-9]*)\" Phase2= \"([0-9][0-9]*)\" Total= \"([0-9][0-9]*)\" ");
              
                private static final Pattern CARD_CLEANING_REGEX_4 = Pattern.compile("  final cards cleaned: Phase1= \"([0-9][0-9]*)\" Phase2= \"([0-9][0-9]*)\" Total= \"([0-9][0-9]*)\" ");
              
                private static final String[] IS_PARSEABLE_CRITERIA_STRINGS = { 
                  "<Alloc", "tlh allocation", 
                  "Card cleaning for", "Concurrent mark analysis" };
              
                private static final String ENTRY = Messages.getString("TGCParserImpl.entry");
              
                private Map<String, TupleDataBuilder> tuples = null;
                private YDataAxis numberAxis;
                private static final String CATEGORY = "TGC";
                private static final DataFactory factory = DataFactory.getFactory("TGC");
              
                private static final XAxis X_AXIS = factory.createXAxis("TGCParserImpl.entry", 
                  ENTRY, VGCAxes.GC_NUMBER);
                private YDataAxis[] allocLine1Axes;
                private YDataAxis[] allocLine2Axes;
                private int gcNumber = -1;
              
                private static final Logger TRACE = LogFactory.getTrace(TGCParserImpl.class);
                private XDataAxis xAxis;
              
                public boolean isParseableSource(Source source)
                  throws GCAndMemoryVisualizerException
                {
                  TRACE.entering(this.className, "isParseableSource");
                  boolean answer = source
                    .containsWithinReasonableDistanceOfStart(IS_PARSEABLE_CRITERIA_STRINGS);
                  TRACE.exiting(this.className, "isParseableSource");
                  return answer;
                }
              
                public SourceData parse(Source source, OutputProperties properties)
                  throws GCAndMemoryVisualizerException
                {
                  TRACE.entering(this.className, "parse");
              
                  this.gcNumber = 0;
                  SourceData sourceData = null;
              
                  this.numberAxis = VGCAxes.prepareNumberAxis(properties);
                  YDataAxis bytesAxis = VGCAxes.prepareBytesAxis(properties);
                  this.allocLine1Axes = new YDataAxis[] { this.numberAxis, bytesAxis, bytesAxis };
              
                  this.allocLine2Axes = new YDataAxis[] { this.numberAxis, this.numberAxis, bytesAxis, bytesAxis };
              
                  this.xAxis = X_AXIS.createDataAxis(source, false, properties);
              
                  this.tuples = new HashMap();
                  try
                  {
                    String variantIdentifier = source.getVariantIdentifier();
                    sourceData = SourceDataFactory.getFactory().createSourceData(
                      variantIdentifier);
              
                    BufferedReader reader = source.getSafeBufferedStreamReader();
              
                    String line = reader.readLine();
              
                    while (line != null)
                    {
                      handleLine(sourceData, line, reader);
              
                      line = reader.readLine();
                    }
              
                    Iterator labels = this.tuples.keySet().iterator();
              
                    while (labels.hasNext()) {
                      String label = (String)labels.next();
                      TupleData tupleData = (TupleData)this.tuples.get(label);
              
                      sourceData.addData(tupleData, DataLevel.VARIANT);
                    }
                    reader.close();
                  }
                  catch (IOException ie) {
                    throw new GCAndMemoryVisualizerException(ie);
                  }
                  TRACE.exiting(this.className, "parse");
                  return sourceData;
                }
              
                private void handleLine(SourceData sourceData, String line, BufferedReader reader)
                  throws IOException
                {
                  if (line.length() > 0) {
                    Matcher allocs = ALLOC_REGEX.matcher(line);
                    if (allocs.matches()) {
                      handleAlloc(allocs, reader.readLine());
                    }
                    else
                    {
                      Matcher concurrents = CONCURRENT_ANALYSIS_REGEX.matcher(line);
              
                      if (concurrents.matches())
                      {
                        handleConcurrentAnalysis(concurrents);
                      } else {
                        Matcher cleaning = CARD_CLEANING_REGEX.matcher(line);
                        if (cleaning.matches())
                        {
                          handleCardCleaning(cleaning, reader.readLine(), 
                            reader.readLine(), reader.readLine());
                        }
                        else if (TRACE.isLoggable(Level.FINEST))
                          TRACE.finest("TGC parser doesn't know how to parse " + 
                            line);
                      }
                    }
                  }
                }
              
                private void handleAlloc(Matcher line1, String line2)
                {
                  this.gcNumber += 1;
                  this.xAxis.setX(this.gcNumber);
              
                  int index = 0;
                  String label = "TGCParserImpl.tlh.allocations";
                  String group = line1.group(index + 1);
                  TupleData tupleData = getTupleData(label, this.allocLine1Axes, index);
                  addPoint(group, tupleData);
              
                  index++;
                  label = "TGCParserImpl.allocated.tlh.size";
                  group = line1.group(index + 1);
                  tupleData = getTupleData(label, this.allocLine1Axes, index);
                  addPoint(group, tupleData);
              
                  index++;
                  label = "TGCParserImpl.allocated.tlh.discard";
              
                  group = line1.group(index + 1);
                  tupleData = getTupleData(label, this.allocLine1Axes, index);
                  addPoint(group, tupleData);
              
                  if ((line2 != null) && (line2.length() > 0)) {
                    Matcher nonTLHallocs = NON_TLH_ALLOC_REGEX.matcher(line2);
                    if (nonTLHallocs.matches()) {
                      index = 0;
                      label = "TGCParserImpl.non.tlh.allocations";
                      group = nonTLHallocs.group(index + 1);
                      tupleData = getTupleData(label, this.allocLine2Axes, index);
                      addPoint(group, tupleData);
              
                      index++;
                      label = "TGCParserImpl.non.tlh.search";
                      group = nonTLHallocs.group(index + 1);
                      tupleData = getTupleData(label, this.allocLine2Axes, index);
                      addPoint(group, tupleData);
              
                      index++;
                      label = "TGCParserImpl.non.tlh.size";
                      group = nonTLHallocs.group(index + 1);
                      tupleData = getTupleData(label, this.allocLine2Axes, index);
                      addPoint(group, tupleData);
              
                      index++;
                      label = "TGCParserImpl.non.tlh.discard";
                      group = nonTLHallocs.group(index + 1);
                      tupleData = getTupleData(label, this.allocLine2Axes, index);
                      addPoint(group, tupleData);
                    }
                  }
                }
              
                private TupleData getTupleData(String label, YDataAxis[] axes, int index)
                {
                  return getTupleData(label, axes[index]);
                }
              
                private TupleData getTupleData(String label, YDataAxis yAxis) {
                  TupleData tupleData = (TupleData)this.tuples.get(label);
                  if (tupleData == null)
                  {
                    AxisPair axisPair = factory.createAxisPair(this.xAxis, yAxis);
                    TupleDataBuilder newTupleData = factory.createTupleData(label, 
                      Messages.getString(label), axisPair);
                    newTupleData.setTupleMetaData(new TupleMetaData(TupleMetaData.TableType.NONE, 
                      null));
                    this.tuples.put(label, newTupleData);
                    tupleData = newTupleData;
                  }
              
                  return tupleData;
                }
              
                private final void addPoint(String group1, TupleData tupleData) {
                  double yvalue = Double.parseDouble(group1);
                  if (this.gcNumber > -1)
                  {
                    tupleData.addDataPoint(yvalue, "");
                  }
                }
              
                private void handleConcurrentAnalysis(Matcher line1)
                {
                  this.gcNumber += 1;
                  this.xAxis.setX(this.gcNumber);
              
                  String label = "TGCParserImpl.total.allocations";
                  String group = line1.group(1);
                  TupleData tupleData = getTupleData(label, this.numberAxis);
                  addPoint(group, tupleData);
              
                  for (int index = 1; index < 6; index++) {
                    label = ALLOCATIONS_PAYING_TAX[index];
                    group = line1.group(index + 1);
                    tupleData = getTupleData(label, this.numberAxis);
                    addPoint(group, tupleData);
                  }
                }
              
                private void handleCardCleaning(Matcher line1, String line2, String line3, String line4)
                {
                  this.gcNumber = Integer.parseInt(line1.group(1));
                  this.xAxis.setX(this.gcNumber);
              
                  Matcher line3Matcher = CARD_CLEANING_REGEX_3.matcher(line3);
                  line3Matcher.matches();
                  String label = "TGCParserImpl.concurrent.cards.cleaned.phase1";
                  String group = line3Matcher.group(1);
                  TupleData tupleData = getTupleData(label, this.numberAxis);
                  addPoint(group, tupleData);
              
                  label = "TGCParserImpl.concurrent.cards.cleaned.phase2";
                  group = line3Matcher.group(2);
                  tupleData = getTupleData(label, this.numberAxis);
                  addPoint(group, tupleData);
              
                  label = "TGCParserImpl.concurrent.cards.cleaned.total";
                  group = line3Matcher.group(3);
                  tupleData = getTupleData(label, this.numberAxis);
                  addPoint(group, tupleData);
              
                  Matcher line4Matcher = CARD_CLEANING_REGEX_4.matcher(line4);
                  line4Matcher.matches();
                  label = "TGCParserImpl.final.cards.cleaned.phase1";
                  group = line4Matcher.group(1);
                  tupleData = getTupleData(label, this.numberAxis);
                  addPoint(group, tupleData);
              
                  label = "TGCParserImpl.final.cards.cleaned.phase2";
                  group = line4Matcher.group(2);
                  tupleData = getTupleData(label, this.numberAxis);
                  addPoint(group, tupleData);
              
                  label = "TGCParserImpl.final.cards.cleaned.total";
                  group = line4Matcher.group(3);
                  tupleData = getTupleData(label, this.numberAxis);
                  addPoint(group, tupleData);
                }
              
                protected String stringAfter(String line, String substring)
                {
                  int i2 = line.lastIndexOf(substring);
                  return line.substring(i2 + substring.length());
                }
              
                protected String stringBetween(String line, char c1, char c2)
                {
                  int i2 = line.lastIndexOf(c2);
                  return line.substring(line.lastIndexOf(c1, i2) + 1, i2);
                }
              
                protected String stringBetween(String line, String s1, String s2)
                {
                  if (!contains(line, s1))
                    return null;
                  if (!contains(line, s2))
                  {
                    return null;
                  }
              
                  int i2 = line.lastIndexOf(s2);
                  return line.substring(line.lastIndexOf(s1, i2) + s1.length(), i2);
                }
              
                protected boolean contains(String string1, String string2)
                {
                  if (string1 != null) {
                    return string1.indexOf(string2) != -1;
                  }
                  return false;
                }
              }

